"""
数据大屏路由层

提供数据大屏相关的API接口与WebSocket推送：
1. KPI指标统计
2. 高负载台区列表
3. 最近告警列表
4. 告警统计
5. WebSocket实时推送
6. 缓存管理
"""

import asyncio
import json
from datetime import datetime
from typing import Dict, Any, Optional

from fastapi import APIRouter, Depends, WebSocket, Query, WebSocketDisconnect

from app.common.auth import RequireTelemetryView
from app.common.common import DbDep, AuditLogDep, DashboardServiceDep
from app.common.websocket_manager import get_websocket_manager
from app.crud.alarm_crud import safe_slice
from app.schemas.dashboard_schemas import (
	KPIMetricsResponse,
	LoadEnergyMetricsResponse,
	HighLoadStationResponse,
	RecentAlertsResponse,
	AlarmStatsResponse,
	AlertWebSocketData,
	DashboardQueryRequest,
	HighLoadStationQueryRequest,
	RecentAlertsQueryRequest,
	AlertStreamItem
)
from app.schemas.user_schemas import UserInDB
from common.logger import create_logger
from common.response import ApiResponse

logger = create_logger(__name__)

router = APIRouter(prefix="/dashboard", tags=["数据大屏"])


# ==================== 工具函数 ====================

def serialize_datetime(obj: Any) -> Any:
	"""递归将 datetime 转换为 ISO 格式字符串"""
	if isinstance(obj, datetime):
		return obj.isoformat()
	if isinstance(obj, dict):
		return {k: serialize_datetime(v) for k, v in obj.items()}
	if isinstance(obj, list):
		return [serialize_datetime(i) for i in obj]
	return obj


def build_websocket_message(msg_type: str, data: Dict[str, Any], timestamp: Optional[datetime] = None) -> Dict[str, Any]:
	"""统一构造 WebSocket 消息，确保序列化安全"""
	return {
		"type": msg_type,
		"data": serialize_datetime(data),
		"timestamp": (timestamp or datetime.now()).isoformat()
	}


# ==================== 数据大屏API接口 ====================

@router.get(
	"/load-energy",
	response_model=ApiResponse[LoadEnergyMetricsResponse],
	summary="获取负荷与供电量指标"
)
async def get_load_energy_metrics(
		db: DbDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView),
		force_refresh: bool = Query(False, description="强制刷新缓存"),
		province_code: Optional[str] = Query(None, description="省编码"),
		city_code: Optional[str] = Query(None, description="市编码"),
		area_code: Optional[str] = Query(None, description="区编码"),
		station_id: Optional[int] = Query(None, description="台区ID")
) -> ApiResponse[LoadEnergyMetricsResponse]:
	"""获取负荷与供电量指标（缓存120秒）"""
	try:
		logger.info(f"获取负荷与供电量指标，强制刷新: {force_refresh}")

		# 构建请求模型
		request = DashboardQueryRequest(
			province_code=province_code,
			city_code=city_code,
			area_code=area_code,
			station_id=station_id,
			force_refresh=force_refresh
		)

		load_energy_data = await service.get_load_energy_metrics(db, request)
		return ApiResponse.success(load_energy_data, "负荷与供电量指标获取成功")
	except Exception as e:
		logger.error(f"获取负荷与供电量指标失败: {e}")
		return ApiResponse.error("获取负荷与供电量指标失败", code=500)


@router.get(
	"/kpi",
	response_model=ApiResponse[KPIMetricsResponse],
	summary="获取KPI指标统计"
)
async def get_kpi_metrics(
		db: DbDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView),
		force_refresh: bool = Query(False, description="强制刷新缓存"),
		province_code: Optional[str] = Query(None, description="省编码"),
		city_code: Optional[str] = Query(None, description="市编码"),
		area_code: Optional[str] = Query(None, description="区编码"),
		start_time: Optional[datetime] = Query(None, description="开始时间"),
		end_time: Optional[datetime] = Query(None, description="结束时间"),
		station_id: Optional[int] = Query(None, description="台区ID")
) -> ApiResponse[KPIMetricsResponse]:
	"""获取KPI指标统计（缓存30秒）"""
	try:
		logger.info(f"获取KPI指标，强制刷新: {force_refresh}")

		# 构建请求模型
		request = DashboardQueryRequest(
			province_code=province_code,
			city_code=city_code,
			area_code=area_code,
			start_time=start_time,
			end_time=end_time,
			station_id=station_id,
			force_refresh=force_refresh
		)

		kpi_data = await service.get_kpi_metrics(db, request)
		return ApiResponse.success(kpi_data, "KPI指标获取成功")
	except Exception as e:
		logger.error(f"获取KPI指标失败: {e}")
		return ApiResponse.error("获取KPI指标失败", code=500)


@router.get(
	"/stations/top",
	response_model=ApiResponse[HighLoadStationResponse],
	summary="获取高负载台区列表"
)
async def get_high_load_stations(
		db: DbDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView),
		limit: int = Query(10, ge=1, le=50, description="返回数量"),
		sort_by: str = Query("load_percentage", description="排序字段"),
		order: str = Query("desc", description="排序方向"),
		force_refresh: bool = Query(False, description="强制刷新")
) -> ApiResponse[HighLoadStationResponse]:
	"""获取高负载台区列表"""
	try:
		logger.info(f"获取高负载台区，参数: limit={limit}, sort_by={sort_by}, order={order}")

		# 构建请求模型
		request = HighLoadStationQueryRequest(
			limit=limit,
			sort_by=sort_by,
			order=order,
			force_refresh=force_refresh
		)

		data = await service.get_high_load_stations(db, request)
		return ApiResponse.success(data, "高负载台区列表获取成功")
	except Exception as e:
		logger.error(f"获取高负载台区失败: {e}")
		return ApiResponse.error("获取高负载台区失败", code=500)


@router.get(
	"/alerts/recent",
	response_model=ApiResponse[RecentAlertsResponse],
	summary="获取最近告警列表"
)
async def get_recent_alerts(
		db: DbDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView),
		limit: int = Query(20, ge=1, le=100, description="返回数量"),
		force_refresh: bool = Query(False, description="强制刷新")
) -> ApiResponse[RecentAlertsResponse]:
	"""获取最近告警列表"""
	try:
		logger.info(f"获取最近告警，limit={limit}")

		# 构建请求模型
		request = RecentAlertsQueryRequest(
			limit=limit,
			force_refresh=force_refresh
		)

		data = await service.get_recent_alerts(db, request)
		return ApiResponse.success(data, "最近告警列表获取成功")
	except Exception as e:
		logger.error(f"获取最近告警失败: {e}")
		return ApiResponse.error("获取最近告警失败", code=500)


@router.get(
	"/alarms/stats",
	response_model=ApiResponse[AlarmStatsResponse],
	summary="获取告警统计数据"
)
async def get_alarm_statistics(
		db: DbDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView),
		force_refresh: bool = Query(False, description="强制刷新")
) -> ApiResponse[AlarmStatsResponse]:
	"""获取告警统计数据"""
	try:
		logger.info("获取告警统计数据")
		data = await service.get_alarm_statistics(db, force_refresh=force_refresh)
		return ApiResponse.success(data, "告警统计获取成功")
	except Exception as e:
		logger.error(f"获取告警统计失败: {e}")
		return ApiResponse.error("获取告警统计失败", code=500)


@router.post(
	"/cache/refresh",
	response_model=ApiResponse[Dict[str, str]],
	summary="刷新缓存"
)
async def refresh_cache(
		db: DbDep,
		_audit: AuditLogDep,
		service: DashboardServiceDep,
		_: UserInDB = Depends(RequireTelemetryView)
) -> ApiResponse[Dict[str, str]]:
	"""刷新数据大屏缓存"""
	try:
		logger.info("开始刷新数据大屏缓存")
		await service.refresh_all_cache(db)
		return ApiResponse.success({"message": "缓存刷新成功"}, "缓存刷新成功")
	except Exception as e:
		logger.error(f"刷新缓存失败: {e}")
		return ApiResponse.error("刷新缓存失败", code=500)


@router.delete(
	"/cache",
	response_model=ApiResponse[Dict[str, str]],
	summary="清除缓存"
)
async def clear_cache(
		service: DashboardServiceDep,
		_audit: AuditLogDep,
		_: UserInDB = Depends(RequireTelemetryView)
) -> ApiResponse[Dict[str, str]]:
	"""清除数据大屏缓存"""
	try:
		logger.info("开始清除数据大屏缓存")
		service.clear_cache()
		return ApiResponse.success({"message": "缓存清除成功"}, "缓存清除成功")
	except Exception as e:
		logger.error(f"清除缓存失败: {e}")
		return ApiResponse.error("清除缓存失败", code=500)


@router.get(
	"/websocket/stats",
	response_model=ApiResponse[Dict[str, Any]],
	summary="获取WebSocket连接统计"
)
async def get_websocket_stats(_: UserInDB = Depends(RequireTelemetryView)) -> ApiResponse[Dict[str, Any]]:
	"""获取WebSocket连接统计信息"""
	try:
		stats = get_websocket_manager().get_connection_stats()
		return ApiResponse.success(stats, "WebSocket统计获取成功")
	except Exception as e:
		logger.error(f"获取WebSocket统计失败: {e}")
		return ApiResponse.error("获取WebSocket统计失败", code=500)


# ==================== WebSocket实时推送接口 ====================

@router.websocket("/ws/alerts")
async def websocket_alerts(websocket: WebSocket):
	"""WebSocket实时告警推送端点"""
	manager = get_websocket_manager()
	if not await manager.connect(websocket):
		return

	try:
		manager.subscribe_topic(websocket, "alerts")

		while True:
			try:
				data = await asyncio.wait_for(websocket.receive_text(), timeout=20)
				if data == "ping":
					await websocket.send_json({
						"type": "pong",
						"timestamp": datetime.now().isoformat()
					})

				elif data == "subscribe_alerts":
					manager.subscribe_topic(websocket, "alerts")
					await websocket.send_json({"type": "subscribed", "topic": "alerts"})
				elif data == "unsubscribe_alerts":
					manager.unsubscribe_topic(websocket, "alerts")
					await websocket.send_json({"type": "unsubscribed", "topic": "alerts"})
			except asyncio.TimeoutError:
				await websocket.send_text("")  # 静默心跳
	except WebSocketDisconnect:
		logger.info(f"WebSocket 客户端主动断开连接")
	except Exception as e:
		logger.error(f"WebSocket 异常: {e}", exc_info=True)
	finally:
		await manager.disconnect(websocket)
		logger.info("WebSocket连接已关闭")


def _create_default_alert_data(existing_data: Dict[str, Any]) -> Dict[str, Any]:
	"""创建默认的告警数据

	Args:
		existing_data: 已有的数据

	Returns:
		Dict[str, Any]: 完整的告警数据
	"""
	from common.alarm_utils import format_alarm_message

	# 优先从已有数据中获取meter_code和rule_name
	rule_name = existing_data.get('rule_name', '')
	meter_code = existing_data.get('meter_code') or 'UNKNOWN'
	alarm_id = existing_data.get('alarm_id', 0)
	alarm_type = existing_data.get('alarm_type', 'UNKNOWN')

	# 记录调试信息
	logger.debug(f"创建默认告警数据: alarm_id={alarm_id}, meter_code={meter_code}, alarm_type={alarm_type}, rule_name={rule_name}")

	# 优先使用rule_name，如果不存在再使用工具函数格式化
	if rule_name and rule_name.strip():
		formatted_message = f"{meter_code}告警: {rule_name}"
	else:
		# 使用工具函数格式化告警消息
		formatted_message = format_alarm_message(meter_code, alarm_type)

	# 基本默认值
	default_data = {
		'id': alarm_id,
		'device_code': meter_code,
		'device_type': '电表',
		'alarm_type': alarm_type,
		'severity': 2,  # 一般级别
		'severity_label': '一般',
		'raised_at': datetime.now(),
		'message': formatted_message,  # 使用格式化后的消息
		'time': datetime.now().strftime('%H:%M:%S')
	}

	# 合并已有数据（保持原有字段优先级）
	for key, value in existing_data.items():
		if key not in default_data or value is not None:
			default_data[key] = value

	return default_data


async def broadcast_new_alert(alert_data: Dict[str, Any], action: str = "new"):
	"""广播告警到所有WebSocket连接

	Args:
		alert_data: 告警数据
		action: 动作类型 (new/recovered/work_order_closed)
	"""
	try:
		from app.schemas.dashboard_schemas import AlertStreamItem

		# 记录原始数据用于调试
		logger.debug(f"广播告警原始数据: {alert_data}")

		meter_code = alert_data.get('meter_code')
		if meter_code:
			logger.info(f"收到告警数据包含 meter_code: {meter_code}")
		else:
			logger.warning(f"告警数据中缺少 meter_code，原始数据: {alert_data}")

		# 验证必要字段
		required_fields = ['id', 'device_code', 'device_type', 'severity', 'severity_label', 'raised_at']
		missing_fields = [field for field in required_fields if field not in alert_data]

		if missing_fields:
			# 数据不完整，使用默认值填充
			logger.info(f"告警数据缺少字段 {missing_fields}，使用默认值填充")
			alert_data = _create_default_alert_data(alert_data)
			logger.info(
				f"填充后的数据: alarm_id={alert_data.get('alarm_id', 'unknown')} device_code={alert_data.get('device_code', 'UNKNOWN')}")
		else:
			logger.debug(f"告警数据完整，直接使用 device_code={alert_data.get('device_code', 'UNKNOWN')}")

		# 最终验证 device_code
		final_device_code = alert_data.get('device_code', 'UNKNOWN')
		if final_device_code == 'UNKNOWN':
			logger.error(f"最终 device_code 仍为 UNKNOWN，请检查数据流: {alert_data}")
		else:
			logger.info(f"最终 device_code: {final_device_code}")

		alert_item = AlertStreamItem(**alert_data)

		# 详细记录AlertStreamItem的构造结果
		logger.info(
			f"AlertStreamItem构造完成: id={alert_item.id}, device_code={alert_item.device_code}, alarm_type={alert_item.alarm_type}")

		# 修复WebSocket消息格式，避免重复嵌套
		alert_websocket_data = AlertWebSocketData(alert=alert_item, action=action)
		msg = build_websocket_message("alert", alert_websocket_data.model_dump(mode="json"),
									  timestamp=alert_item.raised_at)

		# 记录完整的WebSocket消息内容用于调试
		logger.info(f"WebSocket消息构造完成: {json.dumps(msg, default=str, ensure_ascii=False)}")

		# 特别验证 device_code 字段
		try:
			# 修正：正确的消息结构是 msg["data"]["alert"] 而不是 msg["data"]["alert"]["alert"]
			alert_in_msg = msg["data"]["alert"]
			msg_device_code = alert_in_msg.get("device_code", "NOT_FOUND")
			if msg_device_code == "UNKNOWN" or msg_device_code == "NOT_FOUND":
				logger.error(f"❗ WebSocket消息中 device_code 仍为 {msg_device_code}！原始数据: {alert_data}")
				logger.error(f"❗ AlertStreamItem 字段: id={alert_item.id}, device_code={alert_item.device_code}")
			else:
				logger.info(f"✅ WebSocket消息中 device_code 正确: {msg_device_code}")
		except Exception as validate_e:
			logger.error(f"验证WebSocket消息失败: {validate_e}", exc_info=True)

		manager = get_websocket_manager()
		success_count = await manager.broadcast(msg, topic="alerts")
		logger.info(
			f"已广播告警 ({action})：{alert_item.device_code} - {alert_item.alarm_type} 到 {success_count} 个连接")
	except Exception as e:
		logger.error(f"广播告警失败: {e}", exc_info=True)


async def broadcast_kpi_update(kpi_data: Dict[str, Any]):
	"""广播KPI更新到所有WebSocket连接"""
	try:
		msg = build_websocket_message("kpi_update", kpi_data)
		manager = get_websocket_manager()
		success_count = await manager.broadcast(msg, topic="kpi")
		logger.info(f"已广播KPI数据更新到 {success_count} 个连接")
	except Exception as e:
		logger.error(f"广播KPI更新失败: {e}")


__all__ = ["router", "broadcast_new_alert", "broadcast_kpi_update"]
